package cn.zhangfusheng.elasticsearch.thread;

import cn.zhangfusheng.elasticsearch.annotation.ElasticSearchConfig;
import cn.zhangfusheng.elasticsearch.constant.ElasticSearchConstant;
import cn.zhangfusheng.elasticsearch.exception.GlobalSystemException;
import cn.zhangfusheng.elasticsearch.transactional.TransactionalControl;
import lombok.Data;
import org.apache.commons.lang3.RandomUtils;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.tasks.TaskId;

import java.util.Objects;
import java.util.Optional;

/**
 * thread local 缓存的一些信息
 * @author fusheng.zhang
 * @date 2022-04-18 19:48:19
 */
@Data
public class ThreadLocalDetail {

    private static final ThreadLocal<ThreadLocalDetail> ES_THREAD_LOCAL_DETAIL = ThreadLocal.withInitial(ThreadLocalDetail::new);

    /**
     * 是否开启事物
     */
    private TransactionalControl transactionalControl;
    /**
     * 是否查询总命中数量
     */
    private Boolean trackTotalHits;
    /**
     * 启用滚动查询时,每次滚动查询的数据量大小<br/>
     * 必须小于{@link cn.zhangfusheng.elasticsearch.constant.ElasticSearchConstant#MAX_DOC_SIZE}
     */
    private Integer scrollSize;
    /**
     * 滚动查询scrollId 缓存时长
     */
    private Long keepAlive;
    /**
     * 策略
     * WriteRequest.RefreshPolicy.WAIT_UNTIL:修改策略:将此请求保持打开状态，直到刷新使此请求的内容对搜索可见。此刷新策略与高索引和搜索吞吐量兼容，但会导致请求等待回复，直到刷新发生。
     */
    private WriteRequest.RefreshPolicy refreshPolicy;
    /**
     * {@link org.elasticsearch.client.RequestOptions#DEFAULT}
     */
    private RequestOptions requestOptions;

    public static ThreadLocalDetail start(ElasticSearchConfig elasticSearchConfig, RequestOptions requestOptions) {
        ThreadLocalDetail threadLocalDetail = ES_THREAD_LOCAL_DETAIL.get();
        if (elasticSearchConfig.openTransactional()) {
            // 开启事物 并追加等待次数
            if (Objects.isNull(threadLocalDetail.getTransactionalControl())) {
                TaskId taskId = new TaskId(UUIDs.randomBase64UUID(), RandomUtils.nextLong());
                threadLocalDetail.setTransactionalControl(new TransactionalControl(elasticSearchConfig.rollbackFor(), taskId));
            }
            threadLocalDetail.getTransactionalControl().addWaitExecute(1);
        }
        // 刷新策略
        if (Objects.isNull(threadLocalDetail.refreshPolicy)) {
            threadLocalDetail.setRefreshPolicy(elasticSearchConfig.refreshPolicy());
        }
        // 查询全部数据
        if (Objects.isNull(threadLocalDetail.trackTotalHits)) {
            threadLocalDetail.trackTotalHits = elasticSearchConfig.trackTotalHits();
        }
        // 滚动查询 每次查询的条数
        if (Objects.isNull(threadLocalDetail.scrollSize)) {
            if (elasticSearchConfig.scrollSize() > ElasticSearchConstant.MAX_DOC_SIZE) {
                throw new GlobalSystemException("{}.scrollSize > {}", ElasticSearchConfig.class.getName(), ElasticSearchConstant.MAX_DOC_SIZE);
            }
            threadLocalDetail.scrollSize = elasticSearchConfig.scrollSize();
        }
        // 滚动查询scrollId默认缓存时长
        if (Objects.isNull(threadLocalDetail.keepAlive)) {
            threadLocalDetail.keepAlive = elasticSearchConfig.keepAlive();
        }
        // request options
        if (Objects.nonNull(requestOptions)) threadLocalDetail.requestOptions = requestOptions;
        return threadLocalDetail;
    }

    public static void remove() {
        ES_THREAD_LOCAL_DETAIL.remove();
    }

    public static boolean addRequest(DocWriteRequest<?>... requests) {
        TransactionalControl transactionalControl = ES_THREAD_LOCAL_DETAIL.get().getTransactionalControl();
        if (Objects.nonNull(transactionalControl)) {
            return transactionalControl.addRequest(requests);
        }
        return false;
    }

    public static Optional<TaskId> getTransactionTaskId() {
        TransactionalControl transactionalControl = ES_THREAD_LOCAL_DETAIL.get().getTransactionalControl();
        if (Objects.nonNull(transactionalControl)) {
            return Optional.ofNullable(transactionalControl.getTaskId());
        }
        return Optional.empty();
    }

    public static Optional<WriteRequest.RefreshPolicy> getRefreshPolicy() {
        ThreadLocalDetail threadLocalDetail = ES_THREAD_LOCAL_DETAIL.get();
        return Optional.ofNullable(threadLocalDetail.refreshPolicy);
    }

    public Optional<TransactionalControl> transactionaControl() {
        return Optional.ofNullable(ES_THREAD_LOCAL_DETAIL.get().getTransactionalControl());
    }

    public static boolean trackTotalHits() {
        Boolean trackTotalHits = ES_THREAD_LOCAL_DETAIL.get().trackTotalHits;
        return Objects.isNull(trackTotalHits) ? Boolean.FALSE : trackTotalHits;
    }

    public static Optional<Integer> scrollSize() {
        return Optional.ofNullable(ES_THREAD_LOCAL_DETAIL.get().scrollSize);
    }

    public static Optional<Long> keepAlive() {
        return Optional.ofNullable(ES_THREAD_LOCAL_DETAIL.get().keepAlive);
    }

    public static RequestOptions requestOptions() {
        return Optional.ofNullable(ES_THREAD_LOCAL_DETAIL.get().requestOptions).orElse(RequestOptions.DEFAULT);
    }
}
